import { createContext, useEffect, useState } from 'react';

// 需要保存的数据
let reducer = undefined;
let state = undefined;
const listeners = [];

// 设置state时遍历执行所有的listeners
const setState = (newState) => {
  state = newState;
  listeners.forEach((fn) => {
    fn(newState);
  });
}

const dispatch = (action) =>  {
  setState(reducer(state, action))
}

const getState = () => {
  return state;
}

const subscribe = (fn) => {
  listeners.push(fn);
  return () => {
    const i = listeners.indexOf(fn);
    listeners.splice(i, 1);
  };
}

// 向外暴露store
export const store = {};
store.getState = getState;
store.subscribe = subscribe;

const compose = (fns) => {
  return fns.reduceRight((pre, cur) => (...args) => cur(pre(...args)))
}

// applyMiddleware可以理解成通过传入的中间件构造出一个加强版的dispatch
export const applyMiddleware = (...middlewareList) => {
  const chain = middlewareList.map(middleware => middleware({ dispatch, getState }));
  return compose(chain)(dispatch);
}

// 创建store，保存传入的初始state和reducer
export const createStore = (_reducer, initState = {}, newDispatch = dispatch) => {
  state = initState;
  reducer = _reducer;
  store.dispatch = newDispatch
  return store;
};

// 比较数据是否发生变化，从而触发render
const isChange = (data, newData) => 
  Object.keys(data).length !== Object.keys(newData).length ||
  Object.keys(data).some((key) => data[key] !== newData[key]);

// connect高阶函数，在组件的属性prop上添加store数据和修改数据的方法
export const connect = (selector, mapDispatch) => (Component) => (props) => {
  const [, setState] = useState({});
  const data = selector ? selector(state) : { state };
  const dispatcher = mapDispatch ? mapDispatch(store.dispatch) : { dispatch: store.dispatch };
  useEffect(
    () =>
      store.subscribe((state) => {
        const newData = selector ? selector(state) : { state };
        if (!isChange(data, newData)) return;
        // 触发组件重新渲染
        setState({});
      }),
    [selector, data],
  );
  return <Component {...props} {...data} {...dispatcher} />;
};

// 返回dispatch
export const useDispatch = () => {
  return store.dispatch;
}

// 订阅，data变化时触发页面更新
export const useSelector = (selector) => {
  const [, setState] = useState({});
  const data = selector ? selector(state) : { state };
  useEffect(
    () =>
      store.subscribe((state) => {
        const newData = selector ? selector(state) : { state };
        if (!isChange(data, newData)) return;
        setState({});
      }),
    [selector],
  );
  return data;
}

// 上下文
const context = createContext(null);
export const Provider = ({ children, store }) => <context.Provider value={store}>{children}</context.Provider>;
